//*****************************************************************************
//
//! \file DrvSPI.c
//! \brief Driver for the SPI Controller.
//! \version v1.04.005
//! \date 09/05/2011
//! \author Nuvoton
//! \copy
//!
//! Copyright (c)  2011, Nuvoton 
//! All rights reserved.
//*****************************************************************************

//*****************************************************************************
//
//! Include related headers
//
//*****************************************************************************
#include "NUC1xx.h"
#include "core_cm0.h"
#include "DrvSPI.h"
#include "DrvSYS.h"


//*****************************************************************************
//
//! Global file scope (static) variables
//
//*****************************************************************************
typedef struct
{
    uint8_t bInUse;
    PFN_DRVSPI_CALLBACK pfnOneTransDoneCallBack; /* Function pointer of the one transaction done interrupt */
    uint32_t u32OneTransDoneUserData;
    PFN_DRVSPI_CALLBACK pfn3WireStartCallBack;   /* Function pointer of the 3-wire SPI start interrupt */
    uint32_t u32ThreeWireStartUserData;
} S_DRVSPI_HANDLE;

static S_DRVSPI_HANDLE g_sSpiHandler[4];

static SPI_T * SPI_PORT[4]={SPI0, SPI1, SPI2, SPI3};

//*****************************************************************************
//
//! Interrupt Handler
//
//*****************************************************************************


//*****************************************************************************
//
//! \brief SPI0 interrupt handler. Clear the SPI interrupt flag and execute the
//!  callback function. 
//!
//! \param None 
//!
//! \return None.  
//
//*****************************************************************************
void SPI0_IRQHandler(void)
{
    if( SPI0->CNTRL.IF == 1 ) /* One transaction done interrupt */
    {
        SPI0->CNTRL.IF = 1;   /* write '1' to clear SPI0 interrupt flag */
    
        if(g_sSpiHandler[0].pfnOneTransDoneCallBack != NULL)
        {
            g_sSpiHandler[0].pfnOneTransDoneCallBack(g_sSpiHandler[0].u32OneTransDoneUserData);
        }
    }
    
    if( SPI0->CNTRL2.SLV_START_INTSTS == 1 ) /* 3-wire SPI start interrupt */
    {
        SPI0->CNTRL2.SLV_START_INTSTS = 1;   /* write '1' to clear SPI0 3-wire start interrupt flag */
    
        if(g_sSpiHandler[0].pfn3WireStartCallBack != NULL)
        {
            g_sSpiHandler[0].pfn3WireStartCallBack(g_sSpiHandler[0].u32ThreeWireStartUserData);
        }
    }
}


//*****************************************************************************
//
//! \brief SPI1 interrupt handler. Clear the SPI interrupt flag and execute the
//!  callback function. 
//!
//! \param None 
//!
//! \return None.  
//
//*****************************************************************************
void SPI1_IRQHandler(void)
{
    if( SPI1->CNTRL.IF == 1 ) /* One transaction done interrupt */
    {
        SPI1->CNTRL.IF = 1;   /* write '1' to clear SPI1 interrupt flag */
    
        if(g_sSpiHandler[1].pfnOneTransDoneCallBack != NULL)
        {
            g_sSpiHandler[1].pfnOneTransDoneCallBack(g_sSpiHandler[1].u32OneTransDoneUserData);
        }
    }
    
    if( SPI1->CNTRL2.SLV_START_INTSTS == 1 ) /* 3-wire SPI start interrupt */
    {
        SPI1->CNTRL2.SLV_START_INTSTS = 1;   /* write '1' to clear SPI1 3-wire start interrupt flag */
    
        if(g_sSpiHandler[1].pfn3WireStartCallBack != NULL)
        {
            g_sSpiHandler[1].pfn3WireStartCallBack(g_sSpiHandler[1].u32ThreeWireStartUserData);
        }
    }
}

//*****************************************************************************
//
//! \brief SPI2 interrupt handler. Clear the SPI interrupt flag and execute the
//!  callback function. 
//!
//! \param None 
//!
//! \return None.  
//
//*****************************************************************************
void SPI2_IRQHandler(void)
{
    if( SPI2->CNTRL.IF == 1 ) /* One transaction done interrupt */
    {
        SPI2->CNTRL.IF = 1;   /* write '1' to clear SPI2 interrupt flag */
    
        if(g_sSpiHandler[2].pfnOneTransDoneCallBack != NULL)
        {
            g_sSpiHandler[2].pfnOneTransDoneCallBack(g_sSpiHandler[2].u32OneTransDoneUserData);
        }
    }
    
    if( SPI2->CNTRL2.SLV_START_INTSTS == 1 ) /* 3-wire SPI start interrupt */
    {
        SPI2->CNTRL2.SLV_START_INTSTS = 1;   /* write '1' to clear SPI2 3-wire start interrupt flag */
    
        if(g_sSpiHandler[2].pfn3WireStartCallBack != NULL)
        {
            g_sSpiHandler[2].pfn3WireStartCallBack(g_sSpiHandler[2].u32ThreeWireStartUserData);
        }
    }
}

//*****************************************************************************
//
//! \brief SPI3 interrupt handler. Clear the SPI interrupt flag and execute the
//!  callback function. 
//!
//! \param None 
//!
//! \return None.  
//
//*****************************************************************************
void SPI3_IRQHandler(void)
{
    if( SPI3->CNTRL.IF == 1 ) /* One transaction done interrupt */
    {
        SPI3->CNTRL.IF = 1;   /* write '1' to clear SPI3 interrupt flag */
    
        if(g_sSpiHandler[3].pfnOneTransDoneCallBack != NULL)
        {
            g_sSpiHandler[3].pfnOneTransDoneCallBack(g_sSpiHandler[3].u32OneTransDoneUserData);
        }
    }
    
    if( SPI3->CNTRL2.SLV_START_INTSTS == 1 ) /* 3-wire SPI start interrupt */
    {
        SPI3->CNTRL2.SLV_START_INTSTS = 1;   /* write '1' to clear SPI3 3-wire start interrupt flag */
    
        if(g_sSpiHandler[3].pfn3WireStartCallBack != NULL)
        {
            g_sSpiHandler[3].pfn3WireStartCallBack(g_sSpiHandler[3].u32ThreeWireStartUserData);
        }
    }
}


//*****************************************************************************
//
//! \brief Configure the operation mode, transfer type and bit length of a
//!  transaction of the specified SPI port. The timing waveform types:
//!
//! DRVSPI_TYPE0:          
//!
//!     CS    --|          Active state           |---
//!                _   _   _   _   _   _   _   _  
//!     CLK   ____| |_| |_| |_| |_| |_| |_| |_| |_____
//!               
//!     Tx    ----| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |---
//!           
//!     Rx    --| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |-----
//!
//!
//! DRVSPI_TYPE1:          
//!
//!     CS    --|          Active state           |---
//!                _   _   _   _   _   _   _   _  
//!     CLK   ____| |_| |_| |_| |_| |_| |_| |_| |_____
//!               
//!     Tx    --| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |-----
//!           
//!     Rx    --| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |-----
//!
//!
//! DRVSPI_TYPE2:          
//!
//!     CS    --|          Active state           |---
//!                _   _   _   _   _   _   _   _  
//!     CLK   ____| |_| |_| |_| |_| |_| |_| |_| |_____
//!               
//!     Tx    ----| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |---
//!           
//!     Rx    ----| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |---
//!
//!
//! DRVSPI_TYPE3:          
//!
//!     CS    --|          Active state           |---
//!                _   _   _   _   _   _   _   _  
//!     CLK   ____| |_| |_| |_| |_| |_| |_| |_| |_____
//!               
//!     Tx    --| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |-----
//!           
//!     Rx    ----| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |---
//!
//!
//! DRVSPI_TYPE4:          
//!
//!     CS    --|          Active state           |---
//!            ___   _   _   _   _   _   _   _   ______ 
//!     CLK       |_| |_| |_| |_| |_| |_| |_| |_|  
//!               
//!     Tx    --| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |------
//!           
//!     Rx    ----| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |----
//!
//!
//! DRVSPI_TYPE5:
//!
//!     CS    --|          Active state           |---
//!            ___   _   _   _   _   _   _   _   ______ 
//!     CLK       |_| |_| |_| |_| |_| |_| |_| |_|  
//!               
//!     Tx    ----| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |----
//!           
//!     Rx    ----| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |----
//!
//!
//! DRVSPI_TYPE6:
//!
//!     CS    --|          Active state           |---
//!            ___   _   _   _   _   _   _   _   ______ 
//!     CLK       |_| |_| |_| |_| |_| |_| |_| |_|  
//!               
//!     Tx    --| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |------
//!           
//!     Rx    --| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |------
//!
//!
//! DRVSPI_TYPE7:
//!
//!     CS    --|          Active state           |---
//!            ___   _   _   _   _   _   _   _   ______ 
//!     CLK       |_| |_| |_| |_| |_| |_| |_| |_|  
//!               
//!     Tx    ----| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |----
//!           
//!     Rx    --| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |----
//!
//!
//! Master / Slave Transfer Type Matching Table
//!
//!     DRVSPI_TYPE0 <==> DRVSPI_TYPE3
//!     DRVSPI_TYPE1 <==> DRVSPI_TYPE1
//!     DRVSPI_TYPE2 <==> DRVSPI_TYPE2
//!     DRVSPI_TYPE3 <==> DRVSPI_TYPE0
//!     DRVSPI_TYPE4 <==> DRVSPI_TYPE7
//!     DRVSPI_TYPE5 <==> DRVSPI_TYPE5
//!     DRVSPI_TYPE6 <==> DRVSPI_TYPE6
//!     DRVSPI_TYPE7 <==> DRVSPI_TYPE4

//!
//! \param eSpiPort : Specify the SPI port
//! \param eMode : Specify the operation mode (eDRVSPI_MASTER/eDRVSPI_SLAVE)
//! \param eType : Specify the transfer type (eDRVSPI_TYPE0 ~ eDRVSPI_TYPE7)
//! \param i32BitLength : Specify the bit length in a transaction (1~32) 
//!
//! \return E_DRVSPI_ERR_INIT: The specified SPI port has been opened before.
//!         E_DRVSPI_ERR_BUSY: The specified SPI port is in busy status.
//!         E_DRVSPI_ERR_BIT_LENGTH: The specified bit length is out of range.
//!         E_SUCCESS: Success.  
//
//*****************************************************************************
int32_t DrvSPI_Open(E_DRVSPI_PORT eSpiPort, E_DRVSPI_MODE eMode, E_DRVSPI_TRANS_TYPE eType, int32_t i32BitLength)
{
    int32_t i32TimeOut;
    
    if(g_sSpiHandler[eSpiPort].bInUse)
    {
        return E_DRVSPI_ERR_INIT;
    }
    
    /* Bit length 1 ~ 32 */
    if((i32BitLength <= 0) || (i32BitLength > 32))
    {
        return E_DRVSPI_ERR_BIT_LENGTH;
    }
   
    if(eSpiPort == eDRVSPI_PORT0)
    {
        SYSCLK->APBCLK.SPI0_EN  =1;
        SYS->IPRSTC2.SPI0_RST   =1;
        SYS->IPRSTC2.SPI0_RST   =0;
    }
    else if(eSpiPort == eDRVSPI_PORT1)
    {
        SYSCLK->APBCLK.SPI1_EN  =1;
        SYS->IPRSTC2.SPI1_RST   =1;
        SYS->IPRSTC2.SPI1_RST   =0;
    }
    else if(eSpiPort == eDRVSPI_PORT2)
    {
        SYSCLK->APBCLK.SPI2_EN  =1;
        SYS->IPRSTC2.SPI2_RST   =1;
        SYS->IPRSTC2.SPI2_RST   =0;
    }
    else
    {
        SYSCLK->APBCLK.SPI3_EN  =1;
        SYS->IPRSTC2.SPI3_RST   =1;
        SYS->IPRSTC2.SPI3_RST   =0;
    }
    
    /* Check busy*/
    i32TimeOut = 0x10000;
    while(SPI_PORT[eSpiPort]->CNTRL.GO_BUSY == 1)
    {
        if(i32TimeOut-- <= 0)
            return E_DRVSPI_ERR_BUSY;
    }
    
    g_sSpiHandler[eSpiPort].bInUse = TRUE;
    g_sSpiHandler[eSpiPort].pfnOneTransDoneCallBack = NULL;
    g_sSpiHandler[eSpiPort].u32OneTransDoneUserData = 0;
    g_sSpiHandler[eSpiPort].pfn3WireStartCallBack = NULL;
    g_sSpiHandler[eSpiPort].u32ThreeWireStartUserData = 0;
    
    /* "i32BitLength = 0" means 32 bits */
    if(i32BitLength == 32)
    {
        i32BitLength = 0;
    }
    SPI_PORT[eSpiPort]->CNTRL.TX_BIT_LEN = i32BitLength;
    
    if(eMode == eDRVSPI_SLAVE)
        SPI_PORT[eSpiPort]->CNTRL.SLAVE = 1;
    else
        SPI_PORT[eSpiPort]->CNTRL.SLAVE = 0;
    
    /* Automatic slave select */
    SPI_PORT[eSpiPort]->SSR.AUTOSS = 1;
        
    /* Timing waveform types */
    if(eType==eDRVSPI_TYPE0)
    {
        SPI_PORT[eSpiPort]->CNTRL.CLKP = 0;
        /* Drive data and latch data at the same edge. Not recommend to use this transfer type. */
        SPI_PORT[eSpiPort]->CNTRL.TX_NEG = 0;
        SPI_PORT[eSpiPort]->CNTRL.RX_NEG = 0;
    }
    else if(eType==eDRVSPI_TYPE1)
    {
        SPI_PORT[eSpiPort]->CNTRL.CLKP = 0;
        /* Drive data at falling-edge of serial clock; latch data at rising-edge of serial clock. */
        SPI_PORT[eSpiPort]->CNTRL.TX_NEG = 1;
        SPI_PORT[eSpiPort]->CNTRL.RX_NEG = 0;
    }
    else if(eType==eDRVSPI_TYPE2)
    {
        SPI_PORT[eSpiPort]->CNTRL.CLKP = 0;
        /* Drive data at rising-edge of serial clock; latch data at falling-edge of serial clock. */
        SPI_PORT[eSpiPort]->CNTRL.TX_NEG = 0;
        SPI_PORT[eSpiPort]->CNTRL.RX_NEG = 1;
    }
    else if(eType==eDRVSPI_TYPE3)
    {
        SPI_PORT[eSpiPort]->CNTRL.CLKP = 0;
        /* Drive data and latch data at the same edge. Not recommend to use this transfer type. */
        SPI_PORT[eSpiPort]->CNTRL.TX_NEG = 1;
        SPI_PORT[eSpiPort]->CNTRL.RX_NEG = 1;
    }
    else if(eType==eDRVSPI_TYPE4)
    {
        SPI_PORT[eSpiPort]->CNTRL.CLKP = 1;
        /* Drive data and latch data at the same edge. Not recommend to use this transfer type. */
        SPI_PORT[eSpiPort]->CNTRL.TX_NEG = 0;
        SPI_PORT[eSpiPort]->CNTRL.RX_NEG = 0;
    }
    else if(eType==eDRVSPI_TYPE5)
    {
        SPI_PORT[eSpiPort]->CNTRL.CLKP = 1;
        /* Drive data at falling-edge of serial clock; latch data at rising-edge of serial clock. */
        SPI_PORT[eSpiPort]->CNTRL.TX_NEG = 1;
        SPI_PORT[eSpiPort]->CNTRL.RX_NEG = 0;
    }
    else if(eType==eDRVSPI_TYPE6)
    {
        SPI_PORT[eSpiPort]->CNTRL.CLKP = 1;
        /* Drive data at rising-edge of serial clock; latch data at falling-edge of serial clock. */
        SPI_PORT[eSpiPort]->CNTRL.TX_NEG = 0;
        SPI_PORT[eSpiPort]->CNTRL.RX_NEG = 1;
    }
    else
    {
        SPI_PORT[eSpiPort]->CNTRL.CLKP = 1;
        /* Drive data and latch data at the same edge. Not recommend to use this transfer type. */
        SPI_PORT[eSpiPort]->CNTRL.TX_NEG = 1;
        SPI_PORT[eSpiPort]->CNTRL.RX_NEG = 1;
    }

    return E_SUCCESS;
}

//*****************************************************************************
//
//! \brief Close the specified SPI module and disable the SPI interrupt. 
//!
//! \param eSpiPort :  Specify the SPI port. 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_Close(E_DRVSPI_PORT eSpiPort)
{
    int32_t i32TimeOut;
    
    g_sSpiHandler[eSpiPort].bInUse = FALSE;
    g_sSpiHandler[eSpiPort].pfnOneTransDoneCallBack = NULL;
    g_sSpiHandler[eSpiPort].u32OneTransDoneUserData = 0;
    g_sSpiHandler[eSpiPort].pfn3WireStartCallBack = NULL;
    g_sSpiHandler[eSpiPort].u32ThreeWireStartUserData = 0;

    /* Check SPI state */
    i32TimeOut = 0x10000;
    while(SPI_PORT[eSpiPort]->CNTRL.GO_BUSY == 1)
    {
        if(i32TimeOut-- <= 0)
            break;
    }
   
   if(eSpiPort == eDRVSPI_PORT0)
    {
        NVIC_DisableIRQ(SPI0_IRQn);
        SYS->IPRSTC2.SPI0_RST=1;
        SYS->IPRSTC2.SPI0_RST=0;
        SYSCLK->APBCLK.SPI0_EN=0;
    }
    else if(eSpiPort == eDRVSPI_PORT1)
    {
        NVIC_DisableIRQ(SPI1_IRQn);
        SYS->IPRSTC2.SPI1_RST=1;
        SYS->IPRSTC2.SPI1_RST=0;
        SYSCLK->APBCLK.SPI1_EN=0;
    }
    else if(eSpiPort == eDRVSPI_PORT2)
    {
        NVIC_DisableIRQ(SPI2_IRQn);
        SYS->IPRSTC2.SPI2_RST=1;
        SYS->IPRSTC2.SPI2_RST=0;
        SYSCLK->APBCLK.SPI2_EN=0;
    }
    else
    {
        NVIC_DisableIRQ(SPI3_IRQn);
        SYS->IPRSTC2.SPI3_RST=1;
        SYS->IPRSTC2.SPI3_RST=0;
        SYSCLK->APBCLK.SPI3_EN=0;
    }
   
}


//*****************************************************************************
//
//! \brief Set 2-bit transfer mode.When enable 2-bit transfer mode,the Tx_NUM must 
//!  be configure as 0x00 (one transaction in one transfer.)
//! \param eSpiPort : Specify the SPI port
//! \param bEnable  : Enable (TRUE) / Disable (FALSE)
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_Set2BitTransferMode(E_DRVSPI_PORT eSpiPort, uint8_t bEnable)
{
    if(bEnable)
    {
        SPI_PORT[eSpiPort]->CNTRL.TWOB = 1;
        SPI_PORT[eSpiPort]->CNTRL.TX_NUM = 0;
    }
    else
        SPI_PORT[eSpiPort]->CNTRL.TWOB = 0;
}


//*****************************************************************************
//
//! \brief Set Dertermine to transfer data with LSB first or MSB first. 
//! \param eSpiPort : Specify the SPI port
//! \param eEndian  : Specify LSB first or MSB first
//!                   (eDRVSPI_LSB_FIRST / eDRVSPI_MSB_FIRST)
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_SetEndian(E_DRVSPI_PORT eSpiPort, E_DRVSPI_ENDIAN eEndian)
{

    if(eEndian == eDRVSPI_LSB_FIRST)
        SPI_PORT[eSpiPort]->CNTRL.LSB = 1;
    else
        SPI_PORT[eSpiPort]->CNTRL.LSB = 0;

}

//*****************************************************************************
//
//! \brief Set the bit length of SPI transfer. 
//! \param eSpiPort : Specify the SPI port
//! \param i32BitLength : Specify the bit length (1~32 bits)
//!
//! \return E_SUCCESS:  Success.
//!         E_DRVSPI_ERR_BIT_LENGTH: The bit length is out of range. 
//
//*****************************************************************************
int32_t DrvSPI_SetBitLength(E_DRVSPI_PORT eSpiPort, int32_t i32BitLength)
{

    if((i32BitLength < 1) || (i32BitLength > 32))
    {
        return E_DRVSPI_ERR_BIT_LENGTH;
    }
    if(i32BitLength == 32)
        i32BitLength = 0;

    SPI_PORT[eSpiPort]->CNTRL.TX_BIT_LEN = i32BitLength;
    
    return E_SUCCESS;
}

//*****************************************************************************
//
//! \brief Enable/disable Byte Reorder function.The Byte Reorder function is 
//!  supported only in 16-, 24- and 32-bit transaction mode.
//! \param eSpiPort : Specify the SPI port
//! \param eOption: the options of Byte Reorder function and Byte Suspend function. 
//! eDRVSPI_BYTE_REORDER_SUSPEND_DISABLE: both Byte Reorder function and Byte Suspend 
//!                                                         function are disabled. 
//! eDRVSPI_BYTE_REORDER_SUSPEND        : both Byte Reorder function and Byte Suspend 
//!                                                         function are enabled. 
//! eDRVSPI_BYTE_REORDER                : only enable the Byte Reorder function.
//! eDRVSPI_BYTE_SUSPEND                : only enable the Byte Suspend function. 
//! The Byte Suspend function is only available in 32-bit transaction.        
//!
//! \return E_SUCCESS:  Success.
//!         E_DRVSPI_ERR_BIT_LENGTH: The bit length is not 16-, 24- or 32-bit.. 
//
//*****************************************************************************
int32_t DrvSPI_SetByteReorder(E_DRVSPI_PORT eSpiPort, E_DRVSPI_BYTE_REORDER eOption)
{
    /* The Byte Suspend function is only available in 32-bit transaction. */
    if( (eOption==eDRVSPI_BYTE_REORDER_SUSPEND)||(eOption==eDRVSPI_BYTE_SUSPEND) )
        if( (SPI_PORT[eSpiPort]->CNTRL.TX_BIT_LEN) != 0 )
            return E_DRVSPI_ERR_BIT_LENGTH;
    /* The Byte Reorder function is supported only in 16-, 24- and 32-bit transaction mode. */
    else if( eOption==eDRVSPI_BYTE_REORDER )
    	if( (SPI_PORT[eSpiPort]->CNTRL.TX_BIT_LEN) % 8 )
        	return E_DRVSPI_ERR_BIT_LENGTH;

    SPI_PORT[eSpiPort]->CNTRL.REORDER = eOption;

    return E_SUCCESS;
}

//*****************************************************************************
//
//! \brief Set the number of clock cycle of the suspend interval. 
//!
//! \param eSpiPort : Specify the SPI port.
//! \param i32Interval : In burst transfer mode, this value specified the delay
//!  clocks between successive transactions. If the Byte Suspend function is
//!  enabled, it specified the delay clocks among each byte. It could be 2~17
//!  which indicate 2~17 SPI clock cycles. In FIFO mode, it could be 2~15 and 0.
//!  0 indicates the maximum suspend interval; 2 indicates the minimum suspend
//!  interval. Please refer to TRM for the actual suspend interval. 
//!
//! \note Only for master mode. The suspend cycle setting is shared with burst 
//!  mode, byte suspend function and FIFO mode. 
//!
//! \return E_DRVSPI_ERR_SUSPEND_INTERVAL: The suspend interval setting is out
//!  of range. E_SUCCESS: Success.  
//
//*****************************************************************************
int32_t DrvSPI_SetSuspendCycle(E_DRVSPI_PORT eSpiPort, int32_t i32Interval)
{
    if( SPI_PORT[eSpiPort]->CNTRL.FIFO==1 ) /* In FIFO mode, it could be 2~15 and 0. */
    {
        if( (i32Interval < 2) || (i32Interval > 15) )
        {
            /* If out of range, specify the maximum suspend cycle and return error code. */
            SPI_PORT[eSpiPort]->CNTRL.SP_CYCLE = 0;
            if(i32Interval==0)
                return E_SUCCESS;
            else
                return E_DRVSPI_ERR_SUSPEND_INTERVAL;
        }
        SPI_PORT[eSpiPort]->CNTRL.SP_CYCLE = i32Interval;
        return E_SUCCESS;
    }
    else /* In burst mode and byte suspend function, it could be 2~17. */
    {
        if((i32Interval < 2) || (i32Interval > 17))
        {
            /* If out of range, specify the maximum suspend cycle and return error code. */
            SPI_PORT[eSpiPort]->CNTRL.SP_CYCLE = 15;
            return E_DRVSPI_ERR_SUSPEND_INTERVAL;
        }
        SPI_PORT[eSpiPort]->CNTRL.SP_CYCLE = i32Interval-2;
        return E_SUCCESS;
    }
}

//*****************************************************************************
//
//! \brief Set the trigger mode of slave select pin. Only for slave mode. 
//!
//! \param eSpiPort : Specify the SPI port
//! \param eSSTriggerMode : Specify the trigger mode. (eDRVSPI_EDGE_TRIGGER or
//!  eDRVSPI_LEVEL_TRIGGER) 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_SetTriggerMode(E_DRVSPI_PORT eSpiPort, E_DRVSPI_SSLTRIG eSSTriggerMode)
{
    SPI_PORT[eSpiPort]->SSR.SS_LTRIG = eSSTriggerMode;
}

//*****************************************************************************
//
//! \brief Set the active level of slave select. 
//!
//! \param eSpiPort : Specify the SPI port
//! \param eSSActType : Select the active type of slave select pin.
//!  eDRVSPI_ACTIVE_LOW_FALLING : Slave select pin is active low in level-trigger
//!  mode; or falling-edge trigger in edge-trigger mode.
//!  eDRVSPI_ACTIVE_HIGH_RISING : Slave select pin is active high in
//!  level-trigger mode; or rising-edge trigger in edge-trigger mode. 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_SetSlaveSelectActiveLevel(E_DRVSPI_PORT eSpiPort, E_DRVSPI_SS_ACT_TYPE eSSActType)
{
    SPI_PORT[eSpiPort]->SSR.SS_LVL = eSSActType;
}

//*****************************************************************************
//
//! \brief Get the level-trigger transmission status. Only for slave mode. 
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return TRUE : The transaction number and the transferred bit length met the
//!  requirements which defines in TX_NUM and TX_BIT_LEN among one transfer.
//!         FALSE: The transaction number or the transferred bit length of one
//!  transaction doesn't meet the requirements.  
//
//*****************************************************************************
uint8_t DrvSPI_GetLevelTriggerStatus(E_DRVSPI_PORT eSpiPort)
{
    if(SPI_PORT[eSpiPort]->SSR.LTRIG_FLAG==1)
        return TRUE;
    else
        return FALSE;
}

//*****************************************************************************
//
//! \brief Enable the automatic slave select function and set the specified
//!  slave select pin. Only for master mode. 
//!
//! \param eSpiPort : Specify the SPI port
//! \param eSlaveSel : Select the slave select pins which will be used. It could
//!  be eDRVSPI_NONE, eDRVSPI_SS0, eDRVSPI_SS1 and eDRVSPI_SS0_SS1. 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_EnableAutoSS(E_DRVSPI_PORT eSpiPort, E_DRVSPI_SLAVE_SEL eSlaveSel)
{
    SPI_PORT[eSpiPort]->SSR.AUTOSS = 1;
    SPI_PORT[eSpiPort]->SSR.SSR = eSlaveSel;
}

//*****************************************************************************
//
//! \brief Disable the Automatic Slave Select function and deselect slave select
//!  pins. Only for master mode. 
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_DisableAutoSS(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->SSR.AUTOSS = 0;
    SPI_PORT[eSpiPort]->SSR.SSR = eDRVSPI_NONE;
}

//*****************************************************************************
//
//! \brief Set the slave select pins. Only for master mode. 
//!
//! \param eSpiPort : Specify the SPI port
//! \param eSlaveSel : In automatic slave select operation, to use this
//!  parameter to select the slave select pins which will be used. The specified
//!  slave select pins will be controlled by hardware. In manual slave select
//!  operation, the specified slave select pins will be set to active state. It
//!  could be eDRVSPI_NONE, eDRVSPI_SS0, eDRVSPI_SS1 or eDRVSPI_SS0_SS1. 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_SetSS(E_DRVSPI_PORT eSpiPort, E_DRVSPI_SLAVE_SEL eSlaveSel)
{
    SPI_PORT[eSpiPort]->SSR.SSR = eSlaveSel;
}

//*****************************************************************************
//
//! \brief Set the specified slave select pins to inactive state. Only for
//!  master mode. 
//!
//! \param eSpiPort : Specify the SPI port
//! \param eSlaveSel : Specify slave select pins It could be eDRVSPI_NONE,
//!  eDRVSPI_SS0, eDRVSPI_SS1 or eDRVSPI_SS0_SS1. 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_ClrSS(E_DRVSPI_PORT eSpiPort, E_DRVSPI_SLAVE_SEL eSlaveSel)
{
    uint32_t u32Reg;
    
    u32Reg = SPI_PORT[eSpiPort]->SSR.SSR;
    u32Reg = u32Reg & (~eSlaveSel);
    SPI_PORT[eSpiPort]->SSR.SSR = u32Reg;
}


//*****************************************************************************
//
//! \brief Check the busy status of the specified SPI port. 
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return TRUE:  The SPI port is in busy. 
//!         FALSE: The SPI port is not in busy. 
//!  
//
//*****************************************************************************
uint8_t DrvSPI_IsBusy(E_DRVSPI_PORT eSpiPort)
{
    return ((SPI_PORT[eSpiPort]->CNTRL.GO_BUSY)?TRUE:FALSE);
}

//*****************************************************************************
//
//! \brief Configure the burst transfer settings. 
//!
//! \param eSpiPort : Specify the SPI port
//! \param i32BurstCnt : Specify the transaction number in one transfer. It
//!  could be 1 or 2.
//! \param i32Interval : Specify the delay clocks between successive
//!  transactions. It could be 2~17. 
//!
//! \return E_DRVSPI_ERR_BURST_CNT: The transaction number is out of range.
//!         E_DRVSPI_ERR_SUSPEND_INTERVAL: The suspend interval setting is out of range. 
//!         E_SUCCESS: Success.  
//
//*****************************************************************************
int32_t DrvSPI_BurstTransfer(E_DRVSPI_PORT eSpiPort, int32_t i32BurstCnt, int32_t i32Interval)
{

    if((i32BurstCnt < 1) || (i32BurstCnt > 2))
    {
        return E_DRVSPI_ERR_BURST_CNT;
    }
    
    if((i32Interval < 2) || (i32Interval > 17))
    {
        return E_DRVSPI_ERR_SUSPEND_INTERVAL;
    }

    SPI_PORT[eSpiPort]->CNTRL.TX_NUM = i32BurstCnt-1;
    SPI_PORT[eSpiPort]->CNTRL.SP_CYCLE = i32Interval-2;

    return E_SUCCESS;
}


//*****************************************************************************
//
//! \brief Configure the SPI clock. Only for master mode.If the DIV_ONE bit is
//!  set to 1, executing this function is unmeaningful.
//!
//! \param eSpiPort : Specify the SPI port
//! \param u32Clock1 : Specify the SPI clock rate in Hz. It's the target clock
//!  rate of SPI base clock and variable clock 1.
//! \param u32Clock2 : Specify the SPI clock rate in Hz. It's the target clock
//!  rate of variable clock 2. 
//!
//! \return The actual clock rate of SPI engine clock is returned. SPI engine
//!  clock rate = APB clock rate / ((DIVIDER + 1) * 2) The actual clock rate may
//!  be different from the target SPI clock rate. For example, if the system
//!  clock rate is 12MHz and the target SPI base clock rate is 7MHz, the actual
//!  SPI clock rate will be 6MHz.  
//
//*****************************************************************************
uint32_t DrvSPI_SetClockFreq(E_DRVSPI_PORT eSpiPort, uint32_t u32Clock1, uint32_t u32Clock2)
{
    uint32_t u32Div;
    uint32_t u32Pclk;
    
    u32Pclk = DrvSYS_GetHCLKFreq();
    
    u32Div = 0xFFFF; /* Initial value */
    
    if(u32Clock2!=0)
    {
        if(u32Clock2>u32Pclk)
            u32Div = 0;
        else
        {
            u32Div = (((u32Pclk / u32Clock2) + 1) >> 1) - 1;
            if(u32Div > 65535)
                u32Div = 65535;
        }
        SPI_PORT[eSpiPort]->DIVIDER.DIVIDER2 = u32Div;
    }
    else
        SPI_PORT[eSpiPort]->DIVIDER.DIVIDER2 = 0xFFFF;
    
    if(u32Clock1!=0)
    {
        if(u32Clock1>u32Pclk)
            u32Div = 0;
        else
        {
            u32Div = (((u32Pclk / u32Clock1) + 1) >> 1) - 1;
            if(u32Div > 0xFFFF)
                u32Div = 0xFFFF;
        }
        SPI_PORT[eSpiPort]->DIVIDER.DIVIDER = u32Div;
    }
    else
        SPI_PORT[eSpiPort]->DIVIDER.DIVIDER = 0xFFFF;

    return ( u32Pclk / ((u32Div+1)*2) );
}


//*****************************************************************************
//
//! \brief Get the SPI engine clock rate in Hz. Only for master mode. 
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return The current SPI bus clock frequency in Hz.  
//
//*****************************************************************************
uint32_t DrvSPI_GetClock1Freq(E_DRVSPI_PORT eSpiPort)
{
    uint32_t u32Div;
    uint32_t u32ApbClock;

    u32ApbClock = DrvSYS_GetHCLKFreq();
    u32Div = SPI_PORT[eSpiPort]->DIVIDER.DIVIDER;
    return ((u32ApbClock >> 1) / (u32Div + 1));   /* SPI_CLK = APB_CLK / ((Divider + 1) * 2) */
}

//*****************************************************************************
//
//! \brief Get the clock rate of variable clock 2 in Hz. Only for master mode. 
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return The frequency of variable clock 2 in Hz.  
//
//*****************************************************************************
uint32_t DrvSPI_GetClock2Freq(E_DRVSPI_PORT eSpiPort)
{
    uint32_t u32Div;
    uint32_t u32ApbClock;

    u32ApbClock = DrvSYS_GetHCLKFreq();
    u32Div = SPI_PORT[eSpiPort]->DIVIDER.DIVIDER2;
    return ((u32ApbClock >> 1) / (u32Div + 1));   /* SPI_CLK = APB_CLK / ((Divider + 1) * 2) */
}


//*****************************************************************************
//
//! \brief Set the variable clock function. Only for master mode. If the bit
//!  pattern of VARCLK is '0', the output frequency of SPICLK is according to
//!  the value of DIVIDER. If the bit pattern of VARCLK is '1', the output
//!  frequency of SPICLK is according to the value of DIVIDER2. 
//!
//! \param eSpiPort : Specify the SPI port
//! \param bEnable : TRUE  -- Enable variable clock function FALSE -- Disable
//!  variable clock function
//! \param u32Pattern : Specify the variable clock pattern 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_SetVariableClockFunction(E_DRVSPI_PORT eSpiPort, uint8_t bEnable, uint32_t u32Pattern)
{
    if(bEnable)
    {
        SPI_PORT[eSpiPort]->CNTRL.VARCLK_EN = 1;
        SPI_PORT[eSpiPort]->VARCLK = u32Pattern;
    }
    else
        SPI_PORT[eSpiPort]->CNTRL.VARCLK_EN = 0;
}


//*****************************************************************************
//
//! \brief Enable the SPI interrupt of the specified SPI port and install the
//!  callback function. 
//!
//! \param eSpiPort : Specify the SPI port
//! \param pfnCallback : The callback function of SPI interrupt.
//! \param u32UserData : The parameter which will be passed to the callback
//!  function. 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_EnableInt(E_DRVSPI_PORT eSpiPort, PFN_DRVSPI_CALLBACK pfnCallback, uint32_t u32UserData)
{
    
    if(pfnCallback != NULL)
    {
        g_sSpiHandler[eSpiPort].pfnOneTransDoneCallBack = pfnCallback;
        g_sSpiHandler[eSpiPort].u32OneTransDoneUserData = u32UserData;
    }
    
    SPI_PORT[eSpiPort]->CNTRL.IE = 1;
    
    if(eSpiPort == eDRVSPI_PORT0)
    {
        NVIC_SetPriority(SPI0_IRQn, (1<<__NVIC_PRIO_BITS) - 2);
        NVIC_EnableIRQ(SPI0_IRQn);
    }
    else if(eSpiPort == eDRVSPI_PORT1)
    {
        NVIC_SetPriority(SPI1_IRQn, (1<<__NVIC_PRIO_BITS) - 2);
        NVIC_EnableIRQ(SPI1_IRQn);
    }
    else if(eSpiPort == eDRVSPI_PORT2)
    {
        NVIC_SetPriority(SPI2_IRQn, (1<<__NVIC_PRIO_BITS) - 2);
        NVIC_EnableIRQ(SPI2_IRQn);
    }
    else
    {
        NVIC_SetPriority(SPI3_IRQn, (1<<__NVIC_PRIO_BITS) - 2);
        NVIC_EnableIRQ(SPI3_IRQn);
    }
}   

/*---------------------------------------------------------------------------------------------------------*/
/* Function: DrvSPI_DisableInt                                                                             */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*    eSpiPort   [in]: Specify the SPI port                                                                */
/*                                                                                                         */
/* Returns:                                                                                                */
/*    None.                                                                                                */
/*                                                                                                         */
/* Description:                                                                                            */
/*    Disable the SPI interrupt.                                                                           */
/*---------------------------------------------------------------------------------------------------------*/
void DrvSPI_DisableInt(E_DRVSPI_PORT eSpiPort)
{
    g_sSpiHandler[eSpiPort].pfnOneTransDoneCallBack = NULL;
    g_sSpiHandler[eSpiPort].u32OneTransDoneUserData = 0;
    
    SPI_PORT[eSpiPort]->CNTRL.IE = 0;

    if(eSpiPort == eDRVSPI_PORT0)
    {
        NVIC_DisableIRQ(SPI0_IRQn);
    }
    else if(eSpiPort == eDRVSPI_PORT1)
    {
        NVIC_DisableIRQ(SPI1_IRQn);
    }
    else if(eSpiPort == eDRVSPI_PORT2)
    {
        NVIC_DisableIRQ(SPI2_IRQn);
    }
    else
    {
        NVIC_DisableIRQ(SPI3_IRQn);
    }
}


//*****************************************************************************
//
//! \brief Returns the SPI interrupt flag 
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return 0: SPI interrupt doesn't occur 1: SPI interrupt occurs  
//
//*****************************************************************************
uint32_t DrvSPI_GetIntFlag(E_DRVSPI_PORT eSpiPort)
{
    return (SPI_PORT[eSpiPort]->CNTRL.IF);
}


//*****************************************************************************
//
//! \brief Clear the SPI interrupt flag 
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return None  
//
//*****************************************************************************
void DrvSPI_ClrIntFlag(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->CNTRL.IF = 1;
}


//*****************************************************************************
//
//! \brief Read data from SPI Rx registers and trigger SPI for next transfer. 
//!
//! \param eSpiPort :  Specify the SPI port
//! \param pu32Data : Store the data got from the SPI bus. 
//!
//! \return TRUE:  The data stored in pu32Data is valid. FALSE: The data stored
//!  in pu32Data is invalid.  
//
//*****************************************************************************
uint8_t DrvSPI_SingleRead(E_DRVSPI_PORT eSpiPort, uint32_t *pu32Data)
{
    if(SPI_PORT[eSpiPort]->CNTRL.GO_BUSY==1)
        return FALSE;

    *pu32Data = SPI_PORT[eSpiPort]->RX[0];
    SPI_PORT[eSpiPort]->CNTRL.GO_BUSY = 1;
    return TRUE;
}


//*****************************************************************************
//
//! \brief Write data to SPI bus and trigger SPI to start transfer. 
//!
//! \param eSpiPort :  Specify the SPI port
//! \param pu32Data :  Store the data which will be transmitted through the SPI
//!  bus. 
//!
//! \return TRUE:  The data stored in pu32Data has been transferred. FALSE: The
//!  SPI is in busy. The data stored in pu32Data has not been transferred.  
//
//*****************************************************************************
uint8_t DrvSPI_SingleWrite(E_DRVSPI_PORT eSpiPort, uint32_t *pu32Data)
{
    if(SPI_PORT[eSpiPort]->CNTRL.GO_BUSY==1)
        return FALSE;

    SPI_PORT[eSpiPort]->TX[0] = *pu32Data;
    SPI_PORT[eSpiPort]->CNTRL.GO_BUSY = 1;
    return TRUE;
}


//*****************************************************************************
//
//! \brief Read two words of data from SPI Rx registers and then trigger SPI for
//!  next transfer. 
//!
//! \param eSpiPort :  Specify the SPI port
//! \param pu32Buf : Store the data got from the SPI bus. 
//!
//! \return TRUE: The data stored in pu32Buf is valid. FALSE: The data stored in
//!  pu32Buf is invalid.  
//
//*****************************************************************************
uint8_t DrvSPI_BurstRead(E_DRVSPI_PORT eSpiPort, uint32_t *pu32Buf)
{
    if(SPI_PORT[eSpiPort]->CNTRL.GO_BUSY==1)
        return FALSE;

    pu32Buf[0] = SPI_PORT[eSpiPort]->RX[0];
    pu32Buf[1] = SPI_PORT[eSpiPort]->RX[1];
    SPI_PORT[eSpiPort]->CNTRL.GO_BUSY = 1;

    return TRUE;
}

//*****************************************************************************
//
//! \brief Write two words of data to SPI bus and then trigger SPI to start
//!  transfer. 
//!
//! \param eSpiPort :  Specify the SPI port
//! \param pu32Buf :  Store the data which will be transmitted through the SPI
//!  bus. 
//!
//! \return TRUE: The data stored in pu32Buf has been transferred. FALSE: The
//!  SPI is in busy. The data stored in pu32Buf has not been transferred.  
//
//*****************************************************************************
uint8_t DrvSPI_BurstWrite(E_DRVSPI_PORT eSpiPort, uint32_t *pu32Buf)
{
    if(SPI_PORT[eSpiPort]->CNTRL.GO_BUSY==1)
        return FALSE;

    SPI_PORT[eSpiPort]->TX[0] = pu32Buf[0];
    SPI_PORT[eSpiPort]->TX[1] = pu32Buf[1];
    SPI_PORT[eSpiPort]->CNTRL.GO_BUSY = 1;

    return TRUE;
}


//*****************************************************************************
//
//! \brief Read data from Rx registers. This function will not trigger a SPI
//!  data transfer. 
//!
//! \param eSpiPort :  Specify the SPI port
//! \param pu32Buf : Store the data got from Rx registers.
//! \param u32DataCount :  The count of data read from Rx registers. 
//!
//! \return The count of data actually read from Rx registers.  
//
//*****************************************************************************
uint32_t DrvSPI_DumpRxRegister(E_DRVSPI_PORT eSpiPort, uint32_t *pu32Buf, uint32_t u32DataCount)
{
    uint32_t i;
    
    if(u32DataCount>2)
        u32DataCount = 2;
    
    for(i=0; i<u32DataCount; i++)
    {
        pu32Buf[i] = SPI_PORT[eSpiPort]->RX[i];
    }

    return u32DataCount;
}


//*****************************************************************************
//
//! \brief Write data to Tx registers. This function will not trigger a SPI data
//!  transfer. 
//!
//! \param eSpiPort :  Specify the SPI port
//! \param pu32Buf :  Store the data which will be written to Tx registers.
//! \param u32DataCount :  The count of data write to Tx registers. 
//!
//! \return The count of data actually written to Tx registers.  
//
//*****************************************************************************
uint32_t DrvSPI_SetTxRegister(E_DRVSPI_PORT eSpiPort, uint32_t *pu32Buf, uint32_t u32DataCount)
{
    uint32_t i;

    if(u32DataCount>2)
        u32DataCount = 2;
    
    for(i=0; i<u32DataCount; i++)
    {
        SPI_PORT[eSpiPort]->TX[i] = pu32Buf[i];
    }

    return u32DataCount;
}


//*****************************************************************************
//
//! \brief Set the GO_BUSY bit to trigger a SPI data trasfer. 
//!
//! \param eSpiPort :  Specify the SPI port 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_SetGo(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->CNTRL.GO_BUSY = 1;
}

//*****************************************************************************
//
//! \brief Clear the GO_BUSY bit. 
//!
//! \param eSpiPort :  Specify the SPI port 
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_ClrGo(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->CNTRL.GO_BUSY = 0;
}


//*****************************************************************************
//
//! \brief Configure the DMA settings. 
//!
//! \param eSpiPort :  Specify the SPI port
//!      eDmaMode   :  Specify the DMA mode.
//!                    eDRVSPI_TX_DMA -- DMA-Transmitting
//!                    eDRVSPI_RX_DMA -- DMA-Receiving 
//!    bEnable         TRUE  -- Enable DMA; 
//!                    FALSE -- Disable DMA.  
//!
//! \return None.  
//
//*****************************************************************************
void DrvSPI_SetPDMA(E_DRVSPI_PORT eSpiPort, E_DRVSPI_DMA_MODE eDmaMode, uint8_t bEnable)
{
    if(eDmaMode==eDRVSPI_TX_DMA)
    {
        if(bEnable)
            SPI_PORT[eSpiPort]->DMA.TX_DMA_GO = 1;
        else
            SPI_PORT[eSpiPort]->DMA.TX_DMA_GO = 0;
    }
    else
    {
        if(bEnable)
            SPI_PORT[eSpiPort]->DMA.RX_DMA_GO = 1;
        else
            SPI_PORT[eSpiPort]->DMA.RX_DMA_GO = 0;
    }
}


//*****************************************************************************
//
//! \brief Enable/disable FIFO mode. If the caller enables FIFO mode, configure
//! the setting of suspend cycle. Only the chips with the part number NUC1x0xxxCx,
//! ex: NUC140VE3CN, can support this function.
//!
//! \param eSpiPort : Specify the SPI port
//! \param bEnable : Enable (TRUE) / Disable (FALSE)
//! \param i32Interval : In FIFO mode, it could be 2~15 and 0. 0 indicates the
//!  maximum suspend interval; 2 indicates the minimum suspend interval. Please
//!  refer to NUC122 TRM for the actual suspend interval. 
//!
//! \return E_DRVSPI_ERR_SUSPEND_INTERVAL: The suspend interval setting is out
//!  of range. E_SUCCESS: Success.  
//
//*****************************************************************************
int32_t DrvSPI_SetFIFOMode(E_DRVSPI_PORT eSpiPort, uint8_t bEnable, int32_t i32Interval)
{
    if( bEnable )
    {
        if( (i32Interval < 2) || (i32Interval > 15) )
        {
            /* If out of range, specify the maximum suspend cycle and return error code. */
            SPI_PORT[eSpiPort]->CNTRL.SP_CYCLE = 0;
            /* Enable the FIFO mode */
            SPI_PORT[eSpiPort]->CNTRL.FIFO = 1;
            if(i32Interval==0)
                return E_SUCCESS;
            else
                return E_DRVSPI_ERR_SUSPEND_INTERVAL;
        }
        /* Set the suspend cycle */
        SPI_PORT[eSpiPort]->CNTRL.SP_CYCLE = i32Interval;
        /* Enable the FIFO mode */
        SPI_PORT[eSpiPort]->CNTRL.FIFO = 1;
        return E_SUCCESS;
    }
    
    /* Disable the FIFO mode */
    SPI_PORT[eSpiPort]->CNTRL.FIFO = 0;
    return E_SUCCESS;
}


//*****************************************************************************
//
//! \brief Check the status of the Rx buffer of the specified SPI port. Only the
//! chips with the part number NUC1x0xxxCx, ex: NUC140VE3CN, can support this 
//! function.
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return TRUE:  Rx buffer is empty. FALSE: Rx buffer is not empty.  
//
//*****************************************************************************
uint8_t DrvSPI_IsRxEmpty(E_DRVSPI_PORT eSpiPort)
{
    return ((SPI_PORT[eSpiPort]->CNTRL.RX_EMPTY)?TRUE:FALSE);
}

//*****************************************************************************
//
//! \brief Check the status of the Rx buffer of the specified SPI port.Only the
//!  chips with the part number NUC1x0xxxCx, ex: NUC140VE3CN, can support this 
//!  function.
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return TRUE:  Rx buffer is full. FALSE: Rx buffer is not full.  
//
//*****************************************************************************
uint8_t DrvSPI_IsRxFull(E_DRVSPI_PORT eSpiPort)
{
    return ((SPI_PORT[eSpiPort]->CNTRL.RX_FULL)?TRUE:FALSE);
}

//*****************************************************************************
//
//! \brief Check the status of the Tx buffer of the specified SPI port.Only the 
//!  chips with the part number NUC1x0xxxCx, ex: NUC140VE3CN, can support this 
//!  function. 
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return TRUE:  Tx buffer is empty. FALSE: Tx buffer is not empty.  
//
//*****************************************************************************
uint8_t DrvSPI_IsTxEmpty(E_DRVSPI_PORT eSpiPort)
{
    return ((SPI_PORT[eSpiPort]->CNTRL.TX_EMPTY)?TRUE:FALSE);
}


//*****************************************************************************
//
//! \brief Check the status of the Tx buffer of the specified SPI port.Only the 
//! chips with the part number NUC1x0xxxCx, ex: NUC140VE3CN, can support this 
//! function.
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return TRUE:  Tx buffer is full. FALSE: Tx buffer is not full.  
//
//*****************************************************************************
uint8_t DrvSPI_IsTxFull(E_DRVSPI_PORT eSpiPort)
{
    return ((SPI_PORT[eSpiPort]->CNTRL.TX_FULL)?TRUE:FALSE);
}

//*****************************************************************************
//
//! \brief Clear the Rx FIFO.Only the chips with the part number NUC1x0xxxCx, 
//! ex:NUC140VE3CN,can support this function.
//!
//! \param eSpiPort : Specify the SPI port
//!
//! \return None
//
//*****************************************************************************
void DrvSPI_ClrRxFIFO(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->FIFO_CTL.RX_CLR = 1;
}

//*****************************************************************************
//
//! \brief Clear the Tx FIFO.Only the chips with the part number NUC1x0xxxCx, 
//! ex:NUC140VE3CN,can support this function.
//!
//! \param eSpiPort : Specify the SPI port
//!
//! \return None
//
//*****************************************************************************
void DrvSPI_ClrTxFIFO(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->FIFO_CTL.TX_CLR = 1;
}

//*****************************************************************************
//
//! \brief enable the DIV_ONE feature. The SPI clock rate is determined by the
//! setting of SPI clock divider.Only the chips with the part number NUC1x0xxxCx, 
//! ex:NUC140VE3CN,can support this function.
//!
//! \param eSpiPort : Specify the SPI port
//!
//! \return None
//
//*****************************************************************************
void DrvSPI_EnableDivOne(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->CNTRL2.DIV_ONE = 1;
}


//*****************************************************************************
//
//! \brief disable the DIV_ONE feature. The SPI clock rate is determined by the
//! setting of SPI clock divider.Only the chips with the part number NUC1x0xxxCx, 
//! ex:NUC140VE3CN,can support this function.
//!
//! \param eSpiPort : Specify the SPI port
//!
//! \return None
//
//*****************************************************************************
void DrvSPI_DisableDivOne(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->CNTRL2.DIV_ONE = 0;
}

//*****************************************************************************
//
//! \brief enable the SPI 3-wire function.Only the chips with the part number 
//! NUC1x0xxxCx,ex:NUC140VE3CN,can support this function.
//!
//! \param eSpiPort : Specify the SPI port
//!
//! \return None
//
//*****************************************************************************
void DrvSPI_Enable3Wire(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->CNTRL2.NOSLVSEL = 1;
}


//*****************************************************************************
//
//! \brief Disable the SPI 3-wire function.Only the chips with the part number 
//! NUC1x0xxxCx,ex:NUC140VE3CN,can support this function.
//!
//! \param eSpiPort : Specify the SPI port
//!
//! \return None
//
//*****************************************************************************
void DrvSPI_Disable3Wire(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->CNTRL2.NOSLVSEL = 0;
}


//*****************************************************************************
//
//! \brief Abort transfer when using 3-wire SPI. Specify the SPI port If using 
//! 3-wire SPI as slave, when slave start interrupt status is set but transfer 
//! done flag doesn't be set over a reasonable time, use this function to abort 
//! this transfer.Only the chips with the part number NUC1x0xxxCx,ex:NUC140VE3CN,
//! can support this function.
//!
//! \param eSpiPort : Specify the SPI port
//!
//! \return None
//
//*****************************************************************************
void DrvSPI_3WireAbort(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->CNTRL2.SLV_ABORT = 1;
}

//*****************************************************************************
//
//! \brief Enable the 3-wire SPI start interruptof the specified SPI port and 
//! install the callback function.Only the chips with the part number NUC1x0xxxCx,
//! ex: NUC140VE3CN, can support this function.
//!
//! \param eSpiPort : Specify the SPI port
//! \param pfnCallback   : The callback function of 3-wire SPI start interrupt.           
//! \param u32UserData:The parameter which will be passed to the callback function. 
//!
//! \return None
//
//*****************************************************************************
void DrvSPI_Enable3WireStartInt(E_DRVSPI_PORT eSpiPort, PFN_DRVSPI_CALLBACK pfnCallback, uint32_t u32UserData)
{
    
    if(pfnCallback != NULL)
    {
        g_sSpiHandler[eSpiPort].pfn3WireStartCallBack = pfnCallback;
        g_sSpiHandler[eSpiPort].u32ThreeWireStartUserData = u32UserData;
    }
    
    SPI_PORT[eSpiPort]->CNTRL2.SSTA_INTEN = 1;
    
    if(eSpiPort == eDRVSPI_PORT0)
    {
        NVIC_SetPriority(SPI0_IRQn, (1<<__NVIC_PRIO_BITS) - 2);
        NVIC_EnableIRQ(SPI0_IRQn);
    }
    else if(eSpiPort == eDRVSPI_PORT1)
    {
        NVIC_SetPriority(SPI1_IRQn, (1<<__NVIC_PRIO_BITS) - 2);
        NVIC_EnableIRQ(SPI1_IRQn);
    }
    else if(eSpiPort == eDRVSPI_PORT2)
    {
        NVIC_SetPriority(SPI2_IRQn, (1<<__NVIC_PRIO_BITS) - 2);
        NVIC_EnableIRQ(SPI2_IRQn);
    }
    else
    {
        NVIC_SetPriority(SPI3_IRQn, (1<<__NVIC_PRIO_BITS) - 2);
        NVIC_EnableIRQ(SPI3_IRQn);
    }
}   


//*****************************************************************************
//
//! \brief Disable the 3-wire SPI start interrupt.Only the chips with the part
//!  number NUC1x0xxxCx, ex: NUC140VE3CN, can support this function.
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return None
//
//*****************************************************************************
void DrvSPI_Disable3WireStartInt(E_DRVSPI_PORT eSpiPort)
{
    g_sSpiHandler[eSpiPort].pfn3WireStartCallBack = NULL;
    g_sSpiHandler[eSpiPort].u32ThreeWireStartUserData = 0;
    
    SPI_PORT[eSpiPort]->CNTRL2.SSTA_INTEN = 0;

    if(eSpiPort == eDRVSPI_PORT0)
    {
        NVIC_DisableIRQ(SPI0_IRQn);
    }
    else if(eSpiPort == eDRVSPI_PORT1)
    {
        NVIC_DisableIRQ(SPI1_IRQn);
    }
    else if(eSpiPort == eDRVSPI_PORT2)
    {
        NVIC_DisableIRQ(SPI2_IRQn);
    }
    else
    {
        NVIC_DisableIRQ(SPI3_IRQn);
    }
}


//*****************************************************************************
//
//! \brief Return the 3-wire SPI start interrupt status.Only the chips with the
//! part number NUC1x0xxxCx, ex: NUC140VE3CN, can support this function.
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return 0: SPI start interrupt doesn't occur
//!         1: SPI start interrupt occurs
//
//*****************************************************************************
uint32_t DrvSPI_Get3WireStartIntFlag(E_DRVSPI_PORT eSpiPort)
{
    return (SPI_PORT[eSpiPort]->CNTRL2.SLV_START_INTSTS);
}


//*****************************************************************************
//
//! \brief Clear the 3-wire SPI start interrupt status.Only the chips with the
//! part number NUC1x0xxxCx, ex: NUC140VE3CN, can support this function.
//!
//! \param eSpiPort : Specify the SPI port 
//!
//! \return None 
//
//*****************************************************************************
void DrvSPI_Clr3WireStartIntFlag(E_DRVSPI_PORT eSpiPort)
{
    SPI_PORT[eSpiPort]->CNTRL2.SLV_START_INTSTS = 1;
}

//*****************************************************************************
//
//! \brief Get the version number of NUC122 SPI driver. 
//!
//! \param None 
//!
//! \return Version number.  
//
//*****************************************************************************
uint32_t DrvSPI_GetVersion(void)
{
    return DRVSPI_VERSION_NUM;
}
